package com.seven.netty.protocol.codec;

import com.seven.netty.protocol.bean.Body;
import com.seven.netty.protocol.bean.Head;
import com.seven.netty.protocol.bean.Message;
import com.seven.netty.protocol.constants.NettyConstant;
import com.seven.netty.protocol.utils.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Triple;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author chendongdong
 * @description 解码器
 * @since 2024/3/6 10:02
 */
@Slf4j
public class MessageDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        int msgLength = byteBuf.readableBytes();
        if (msgLength >= NettyConstant.HEAD_LENGTH) {
            if (msgLength > NettyConstant.MAX_LENGTH) {
                log.warn("message  length {} exceed max length {}", msgLength, NettyConstant.MAX_LENGTH);
                byteBuf.skipBytes(msgLength);
            }
            int begin;
            while (true) {
                //开始的位置
                begin = byteBuf.readerIndex();
                int headIndex = byteBuf.readInt();
                //标记
                byteBuf.markReaderIndex();
                if (headIndex == NettyConstant.HEAD_INDEX) {
                    //每次读4个字节 读到了包头 符合要求
                    break;
                }
                //没读到包头重置读取位置
                byteBuf.resetReaderIndex();
                //略过一个字节重新读
                byteBuf.readByte();
                if (byteBuf.readableBytes() < NettyConstant.HEAD_LENGTH) {
                    //没有读到消息头标记
                    return;
                }
            }
            //消息长度 加上 略过的 标头长度 4
            int tempLength = byteBuf.readableBytes() + 4;
            int crcCode = byteBuf.readInt();
            byte version = byteBuf.readByte();
            long sessionId = byteBuf.readLong();
            byte messageType = byteBuf.readByte();
            int length = byteBuf.readInt();
            if (tempLength < length) {
                //消息的内容长度没有达到预期设定的长度，还原指针重新读
                byteBuf.readerIndex(begin);
                return;
            }
            //读取附属数据
            Triple<Integer, Object, Map<String, Object>> headTriple = getHeadAttachmetsAndLength(byteBuf);
            int headLength = headTriple.getLeft();
            Map<String, Object> attachments = headTriple.getRight();
            //读取body消息体
            Object payload = getBody(byteBuf, length, headLength);

            Head head = Head.builder()
                .headIndex(NettyConstant.HEAD_INDEX)
                .crcCode(crcCode)
                .version(version)
                .sessionId(sessionId)
                .messageType(messageType)
                .length(length)
                .attachments(attachments)
                .build();

            Body body = Body.builder()
                .payload(payload)
                .build();

            Message message = Message.builder()
                .head(head)
                .body(body)
                .build();

            log.debug("message decoder ok, message is = {}", message.toString());

            list.add(message);
        }
    }

    /***
     * 读取附加信息及统计头部长度
     * @param byteBuf
     * @return
     */
    private Triple<Integer, Object, Map<String, Object>> getHeadAttachmetsAndLength(ByteBuf byteBuf) {
        int headLength = NettyConstant.HEAD_LENGTH;
        Map<String, Object> attachments = new HashMap<>();
        if (byteBuf.isReadable()) {
            short size = byteBuf.readShort();
            headLength += 2;
            while (size > 0 && attachments.size() < size) {
                int keyLen = byteBuf.readInt();
                byte[] key = new byte[keyLen];
                byteBuf.readBytes(key, 0, keyLen);
                String keyStr = new String(key, StandardCharsets.UTF_8);

                int valLen = byteBuf.readInt();
                byte[] value = new byte[valLen];
                byteBuf.readBytes(value, 0, valLen);
                Object valObj = ByteUtil.byteToObject(value);

                headLength += 4 + keyLen;
                headLength += 4 + valLen;
                attachments.put(keyStr, valObj);
            }
        }
        return Triple.of(headLength, null, attachments);
    }

    /***
     * 获取payload 内容
     * @param byteBuf
     * @param length
     * @param headLength
     * @return
     */
    private Object getBody(ByteBuf byteBuf, int length, Integer headLength) {
        if (byteBuf.isReadable()) {
            int bodyLen = length - headLength;
            byte[] body = new byte[bodyLen];
            byteBuf.readBytes(body);
            return ByteUtil.byteToObject(body);
        }
        return null;
    }
}
